<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<link rel="stylesheet" href="../style/journal.css" type="text/css" />
<style type="text/css"><!--
.googleadsense {
	margin: 2px;
	padding: 0px;
//--></style><script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-65008-1";
urchinTracker();
</script><title>HTML::Parser 简单解释</title>
</head>
<body>
<a href="index.html">Journal</a>(2005) | <a href="../blog/"><b>Blog</b></a>(2006) | <a href="http://www.fayland.org/cgi-bin/random_link.pl">RandomLink</a> | <a href="AboutFayland.html">WhoAmI</a> | <a href="LiveBookmark.html">LiveBookmark</a> | <a href="http://www.fayland.org/">HomePage</a>
<p><&lt;Previous: <a href="perldoc_ie.html">用 IE 来浏览 perldoc</a>&nbsp;&nbsp;>>Next: <a href="Plan2006.html">Plan for 2006: hang myself on the tree of Perl</a></p>
<h1>HTML::Parser 简单解释</h1>
<div class='content'>
<p>Category: <a href='Modules.html'>Modules</a> &nbsp; Keywords: <b>HTML:;Parser</b></p> <a href="http://search.cpan.org/perldoc?HTML::Parser">HTML::Parser</a> 是一个非常强大的用于处理 html 解析的模块。<p />HTML::Parser 的文档没有一个完整的例子。所以我就把下面的我写在 ShellWeb 里的例子摘取出来，并简单的解释一下。<p /><pre>use HTML::Parser;<p />my $parser = HTML::Parser->new( api_version => 3,<br /> &nbsp; &nbsp;start_h => [\&amp;start, "self, tagname, attr, text"],<br /> &nbsp; &nbsp;text_h  => [\&amp;text,  "self, dtext"],<br /> &nbsp; &nbsp;end_h   => [\&amp;end,   "self, tagname"],<br /> &nbsp; &nbsp;ignore_elements => [qw(script style)],<br />);<p />$parser->parse($content);</pre>new 用于建立实例，api_version => 3 用于指定版本，因为 HTML::Parser 还有老的版本 2，可能以后还会有新的版本。而不同的版本有不同的 API, 所以指定这个是必须的。<br />start_h, text_h, end_h 是三个事件处理过程。分别对应标签的开始，文字（就是没有标签的地方但不包括注释等），标签的关闭。<p />其实完整的事件处理包括 text, start, end, declaration, comment, process, start_document, end_document 和 default.<br />一般用到的只有 text start end.<p /><ul>指定事件所对应的处理方法和参数有如下几种方法：<br /><li>第一种就是如上面所看到的，在事件后面加上 _h, 如 text_h, start_h 这样。</li><li>第二种也是在 new 里做为参数。<br /><pre>$p = HTML::Parser->new(api_version => 3,<br />                        handlers => { text => [\@array, "event,text"],<br />                                      comment => [\@array, "event,text"],<br />                                    });</pre>用 handlers 来定义。<br /><li>第三种是在 new 里不定义，在后面用 handler 来定义。<br /><pre>$p = HTML::Parser->new(...);<br />$p->handler(start =>  \&amp;start, 'attr, attrseq, text' );<br /></pre></ul>这三者接受参数的方法是一样的。第一个为处理方法，第二个为参数。<br />完整的参数参考 perldoc <a href="http://search.cpan.org/perldoc?HTML::Parser">HTML::Parser</a><br />最最常用的为 self, tagname, attr, text<br />不过注意的是不同的事件有不同的参数限制。如果 start 事件可以有 attr 但是 end 事件就没有 attr 了。（因为结束标签里是不会有属性的。）<p />如最开始的例子中我们使用了 start_h => [\&start, "self, tagname, attr, text"],<br />那么接下来我们就定义我们自己的 start 子程序：<br /><pre>sub start {<br /> &nbsp; &nbsp;my ($self, $tag, $attr, $text) = @_;<br /> &nbsp; &nbsp;if ($tag eq 'img') {<br /> &nbsp; &nbsp; &nbsp; &nbsp;$buffer->insert($iter, "[IMG - $attr->{src}]");<br /> &nbsp; &nbsp;} elsif ($tag eq 'pre') {<br /> &nbsp; &nbsp; &nbsp; &nbsp;$self->{'_pre'} = 1;<br /> &nbsp; &nbsp;} elsif ($tag eq 'title') {<br /> &nbsp; &nbsp; &nbsp; &nbsp;$self->{'_tilte'} = 1;<br /> &nbsp; &nbsp;} else {<br /> &nbsp; &nbsp; &nbsp; &nbsp;$buffer->insert($iter, $text);<br /> &nbsp; &nbsp;}<br />}</pre>start 子程序的第一行就是 start_h => [\&start, "self, tagname, attr, text"], 后面所对应的参数。分别对照为 self, 标签名（如 a/img 等），属性（这是一个散列，比如 img 必定有 $attr->{src}），text 指的是整个标签的完整原始内容。<br />看起来就是这么简单。接下来定义 text end 子程序：<br /><pre>sub text {<br /> &nbsp; &nbsp;my ($self, $text) = @_;<br /> &nbsp; &nbsp;$text =~ s/\r\n//sg unless ( defined($self->{'_pre'}) );<br /> &nbsp; &nbsp;if ( defined($self->{'_tilte'}) ) {<br /> &nbsp; &nbsp; &nbsp; &nbsp;$window->set_title("$text - ShellWeb");<br /> &nbsp; &nbsp;} else {<br /> &nbsp; &nbsp; &nbsp; &nbsp;$buffer->insert($iter, $text);<br /> &nbsp; &nbsp;}<br />}<p />sub end {<br /> &nbsp; &nbsp;my ($self, $tag) = @_;<br /> &nbsp; &nbsp;if ($tag eq 'li') {<br /> &nbsp; &nbsp; &nbsp; &nbsp;$buffer->insert($iter, "\n");<br /> &nbsp; &nbsp;} elsif ($tag eq 'p') {<br /> &nbsp; &nbsp; &nbsp; &nbsp;$buffer->insert($iter, "\n");<br /> &nbsp; &nbsp;} <br /> &nbsp; &nbsp;$self->{'_tilte'} = undef if ($tag eq 'title');<br /> &nbsp; &nbsp;$self->{'_pre'} = undef   if ($tag eq 'pre');<br />}</pre>这个完整的程序中间我们加了一点点小小的技巧。因为 text 事件是不知道这个文本外面的标签名的（也就是 text_h 没有 tagname 参数）。<br />这样我们必须在 title/pre 标签的开始时用 $self->{'_title'} 保存它开始了，在 end 时将它释放。<br />这样我们就在 text 事件中知道刚文本是不是包围在 title 或 pre 里面。<p />上面就是大致的 HTML::Parser 分析过程。最重要的是实践，自己设定个结果，然后用 HTML::Parser 实现它。<br />实现不了出了问题看 perldoc <a href="http://search.cpan.org/perldoc?HTML::Parser">HTML::Parser</a> 或者给我发 mail. ;-)</div>
<p><&lt;Previous: <a href="perldoc_ie.html">用 IE 来浏览 perldoc</a>&nbsp;&nbsp;>>Next: <a href="Plan2006.html">Plan for 2006: hang myself on the tree of Perl</a></p>
<p><strong>Options:</strong> <a href='http://del.icio.us/post?title=HTML::Parser%20%E7%AE%80%E5%8D%95%E8%A7%A3%E9%87%8A&url=http://www.fayland.org/journal/HTML_Parser_part1.html'>+Del.icio.us</a></p>

Created on <span class="digit">2005-12-29 14:47:57</span>, Last modified on <span class="digit">2005-12-29 14:51:11</span><br />
Copyright 2004-2005 All Rights Reserved. Powered by <a href="Eplanet.html">Eplanet</a> && <a href='http://catalyst.perl.org'>Catalyst</a> 5.62.
</body>
</html>